﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Diggins.Jigsaw;
using Domains;

namespace Parser
{
    class Evaluator2
    {
        public KeyValuePair<Value, State> Evaluate(Node n, State state)
        {
            switch (n.Label)
            {
                case "Program":
                    {
                        var result = new KeyValuePair<Value, State>(null, null);
                        for (int i = 0; i < n.Count; i++)
                        {
                            result = Evaluate(n[i], state);
                        }
                        return result;
                    }
                case "VarDecl":
                    {
                        var text = (n[0].Text);
                        var pair = Evaluate(n[1], state);

                        if (state.Memory.Value.Count(elem => elem.Key.Value == text) != 0)
                        {
                            var ide = state.Memory.Value.Single(elem => elem.Key.Value == text).Key;
                            state.Memory.Value[ide] = pair.Key;
                        }
                        else
                        {
                            var ide = new Ide(text);
                            state.Memory.Value.Add(ide, pair.Key);
                        }

                        return new KeyValuePair<Value, State>(null, state);
                    }
                case "output":
                    {
                        var pair = Evaluate(n[0], state);
                        state.Output.Value.Add(pair.Key);
                        return new KeyValuePair<Value, State>(null, state);
                    }
                case "If":
                    {
                        Node uselessNode = n[0];
                        var pair = Evaluate(uselessNode[0], state);
                        var descision = pair.Key as Bool;

                        if (descision.Value)
                            return Evaluate(n[1], state);
                        if (n.Count == 3)
                            return Evaluate(n[2], state);
                        else
                            return pair;
                    }
                case "Else":
                    {
                        return Evaluate(n[0], state);
                    }
                case "While":
                    {
                        Node uselessNode = n[0];
                        var pair = Evaluate(uselessNode[0], state);
                        var descision = pair.Key as Bool;

                        if (descision.Value)
                        {
                            pair = Evaluate(n[1], state);
                            pair = Evaluate(n, state);
                        }
                        return pair;
                    }
                case "Block":
                    {
                        var result = new KeyValuePair<Value, State>(null, null);
                        for (int i = 0; i < n.Count; i++)
                        {
                            result = Evaluate(n[i], state);
                        }
                        return result;
                    }
                case "Expr":
                    {
                        switch (n[0].Label)
                        {
                            case "read":
                                {
                                    if (state.Input.Value.Count == 0) return new KeyValuePair<Value, State>(null, null);
                                    return new KeyValuePair<Value, State>(Domains.Input.head(state.Input), state);
                                }
                            case "False":
                                return new KeyValuePair<Value, State>(new Bool(false),state);
                            case "True":
                                return new KeyValuePair<Value, State>(new Bool(true), state);
                            case "Integer":
                                return new KeyValuePair<Value, State>(new Num(Int32.Parse(n[0].Text)), state);
                            case "BinaryExpr":
                                {
                                    switch (n[0][1].Text)
                                    {
                                        case "+":
                                            {
                                                var pair1 = Evaluate(n[0][0], state);
                                                if (!(pair1.Key is Num)) return new KeyValuePair<Value, State>(null, null);
                                                var value1 = pair1.Key as Num;

                                                var pair2 = Evaluate(n[0][2], state);
                                                if (!(pair2.Key is Num)) return new KeyValuePair<Value, State>(null, null);
                                                var value2 = pair2.Key as Num;

                                                return new KeyValuePair<Value, State>(new Num(value1.Value + value2.Value), state);
                                            }
                                        case "=":
                                            {
                                                var pair1 = Evaluate(n[0][0], state);
                                                var pair2 = Evaluate(n[0][2], state);

                                                if (pair1.Key is Bool && pair2.Key is Bool)
                                                {
                                                    var value1 = (pair1.Key as Bool).Value == (pair2.Key as Bool).Value;
                                                    return new KeyValuePair<Value, State>(new Bool(value1), state);
                                                }                                                   
                                                else if (pair1.Key is Num && pair2.Key is Num)
                                                {
                                                    var value1 = (pair1.Key as Num).Value == (pair2.Key as Num).Value;
                                                    return new KeyValuePair<Value, State>(new Bool(value1), state);
                                                }
                                                return new KeyValuePair<Value, State>(null, null);
                                            }
                                        default: throw new Exception("unsupported binary operation");
                                    }
                                }
                            case "PrefixExpr":
                                {
                                    switch (n[0][0].Text)
                                    {
                                        case "not":
                                            {
                                                var uselessNode = n[0][1];
                                                var pair = Evaluate(uselessNode[0], state);

                                                if (!(pair.Key is Bool)) return new KeyValuePair<Value, State>(null, null);
                                                var value1 = pair.Key as Bool;

                                                return new KeyValuePair<Value, State>(new Bool(!value1.Value), state);
                                                
                                            }
                                        default : throw new Exception("unsupported unary operation");
                                    }
                                }
                            case "Identifier":
                                {
                                    var text = (n.Text);
                                    if (state.Memory.Value.Count(elem => elem.Key.Value == text) != 0)
                                    {
                                        var ide = state.Memory.Value.Single(elem => elem.Key.Value == text).Key;
                                        var value = state.Memory.Value[ide];
                                        return new KeyValuePair<Value, State>(value, state);
                                    }
                                    else
                                    {
                                        return new KeyValuePair<Value, State>(null, null);
                                    }
                                }

                        }
                        break;
                    }
                case "Identifier":
                    {
                        var text = (n.Text);
                        if (state.Memory.Value.Count(elem => elem.Key.Value == text) != 0)
                        {
                            var ide = state.Memory.Value.Single(elem => elem.Key.Value == text).Key;
                            var value = state.Memory.Value[ide];
                            return new KeyValuePair<Value, State>(value, state);
                        }
                        else
                        {
                            return new KeyValuePair<Value, State>(null, null);
                        }
                    }
                case "False":
                    return new KeyValuePair<Value, State>(new Bool(false), state);
                case "True":
                    return new KeyValuePair<Value, State>(new Bool(true), state);
                case "Integer":
                    return new KeyValuePair<Value, State>(new Num(Int32.Parse(n.Text)), state);
                default: throw new Exception("unsupported comman or exprecion");
            }
            return new KeyValuePair<Value, State>(null, null);
        }
    }
}
